#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <opencv2/opencv.hpp>  // 添加OpenCV头文件

using namespace cv;  // OpenCV命名空间

int main()
{
    int fd = open("/dev/video0", O_RDWR);
    if(fd < 0){
        perror("打开设备失败");
        return -1;
    }

    // 设置视频格式
    struct v4l2_format vfmt;
    memset(&vfmt, 0, sizeof(vfmt));
    vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vfmt.fmt.pix.width = 1280;
    vfmt.fmt.pix.height = 720;
    vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;  // 使用MJPEG格式
    vfmt.fmt.pix.field = V4L2_FIELD_NONE;

    int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
    if(ret < 0){
        perror("设置格式失败");
        close(fd);
        return -1;
    }

    // 检查实际设置的格式
    printf("实际设置的分辨率: %dx%d\n", 
           vfmt.fmt.pix.width, vfmt.fmt.pix.height);

    // 申请内核缓冲区
    struct v4l2_requestbuffers reqbuffer;
    memset(&reqbuffer, 0, sizeof(reqbuffer));
    reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuffer.count = 4;  // 4个缓冲区
    reqbuffer.memory = V4L2_MEMORY_MMAP;
    
    ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
    if(ret < 0){
        perror("申请空间失败");
        close(fd);
        return -1;
    }

    // 映射缓冲区
    unsigned char *mptr[4];
    unsigned int size[4];
    struct v4l2_buffer mapbuffer;
    memset(&mapbuffer, 0, sizeof(mapbuffer));
    mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    for(int i = 0 ; i < 4 ; i++){
        mapbuffer.index = i;
        ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);
        if(ret < 0){
            perror("查阅内核空间失败");
            close(fd);
            return -1;
        } 
        
        // 内存映射
        mptr[i] = (unsigned char *)mmap(NULL, 
                                       mapbuffer.length,
                                       PROT_READ|PROT_WRITE,
                                       MAP_SHARED, 
                                       fd, 
                                       mapbuffer.m.offset);
        if(mptr[i] == MAP_FAILED){
            perror("内存映射失败");
            close(fd);
            return -1;
        }
        
        size[i] = mapbuffer.length;
        
        // 将缓冲区放入队列
        ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer);
        if(ret < 0){
            perror("放回队列失败");
            close(fd);
            return -1;
        }
    }

    // 开启视频流
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd, VIDIOC_STREAMON, &type);
    if(ret < 0){
        perror("开启流失败");
        close(fd);
        return -1;
    }

    // 创建显示窗口
    namedWindow("Camera View", WINDOW_AUTOSIZE);
    
    struct v4l2_buffer readbuffer;
    memset(&readbuffer, 0, sizeof(readbuffer));
    readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    // 循环捕获和显示
    while(true) {
        // 从队列中取出一个缓冲区
        ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);
        if(ret < 0){
            perror("读取数据失败");
            break;
        }

        // 使用OpenCV解码MJPEG数据
        Mat img = imdecode(Mat(1, readbuffer.length, CV_8UC1, mptr[readbuffer.index]), 
                          IMREAD_COLOR);
        
        if(!img.empty()) {
            // 显示图像
            imshow("Camera View", img);
            
            // 按'q'键退出
            if(waitKey(1) == 'q') {
                break;
            }
        } else {
            printf("图像解码失败\n");
        }

        // 将缓冲区放回队列
        ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);
        if(ret < 0){
            perror("队列放回失败");
            break;
        }
    }

    // 关闭视频流
    ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
    if(ret < 0){
        perror("关闭流失败");
    }

    // 解除映射
    for(int i = 0; i < 4; i++) {
        if(mptr[i]) {
            munmap(mptr[i], size[i]);
        }
    }

    // 清理资源
    destroyAllWindows();
    close(fd);
    return 0;
}
